package yns.springboot.guide.demo.data.redis.driver;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import redis.clients.jedis.Connection;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisClientConfig;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisSentinelPool;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import yns.springboot.guide.demo.configure.data.redis.JedisConfigure;
import yns.springboot.guide.demo.configure.data.redis.JedisSentinelConfigure;
import yns.springboot.guide.demo.configure.data.redis.JedisShardInfoConfigure;

@Configuration
public class JedisClientDriverFactory {
	
	// 官方文档 ： https://redis.github.io/jedis/migration-guides/v6-to-v7/
	
	@Autowired
	private JedisConfigure jedisConfigure;
	
	@Autowired
	private JedisSentinelConfigure jedisSentinelConfigure;
	
	@Autowired
	private JedisShardInfoConfigure jedisShardInfoConfigure;
	
	@Autowired
	private ObjectMapper objectMapper;
	
	
	@Bean
	@Primary
	public Jedis jedisFactory() {
		return new Jedis(jedisConfigure.getHost(),Integer.parseInt(jedisConfigure.getPort()));
	}
	
	@Bean
	public JedisPool jedisPoolFactory() {
		
		GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<Jedis>();
		
		poolConfig.setMaxTotal(Integer.valueOf(jedisConfigure.getMaxTotal()));
		poolConfig.setMaxIdle(Integer.valueOf(jedisConfigure.getMaxIdle()));
		poolConfig.setMaxWait(Duration.ofMillis(Long.valueOf(jedisConfigure.getMaxWait())));
		poolConfig.setMinEvictableIdleDuration(Duration.ofMillis(Long.valueOf(jedisConfigure.getMinEvictableIdle())));
		poolConfig.setNumTestsPerEvictionRun(Integer.valueOf(jedisConfigure.getNumTestsPerEvictionRun()));
		poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(Long.valueOf(jedisConfigure.getTimeBetweenEvictionRunsMillis())));
		poolConfig.setBlockWhenExhausted(Boolean.valueOf(jedisConfigure.getBlockWhenExhausted()));
		
		return new JedisPool(poolConfig,jedisConfigure.getHost(),Integer.parseInt(jedisConfigure.getPort()));
	}
	
	
	//@Bean
	public JedisSentinelPool jedisSentinelPoolFactory() {
		
		String[] urls = jedisSentinelConfigure.getUrls().split(",");
		
		Set<String> sentinels = new HashSet<String>();
		for (int i = 0; i < urls.length; i++) {
			sentinels.add(urls[i]);
		}
		
		GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<Jedis>();
		
		poolConfig.setMaxTotal(Integer.valueOf(jedisConfigure.getMaxTotal()));
		poolConfig.setMaxIdle(Integer.valueOf(jedisConfigure.getMaxIdle()));
		poolConfig.setMaxWait(Duration.ofMillis(Long.valueOf(jedisConfigure.getMaxWait())));
		poolConfig.setMinEvictableIdleDuration(Duration.ofMillis(Long.valueOf(jedisConfigure.getMinEvictableIdle())));
		poolConfig.setNumTestsPerEvictionRun(Integer.valueOf(jedisConfigure.getNumTestsPerEvictionRun()));
		poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(Long.valueOf(jedisConfigure.getTimeBetweenEvictionRunsMillis())));
		poolConfig.setBlockWhenExhausted(Boolean.valueOf(jedisConfigure.getBlockWhenExhausted()));
		
		
		return new JedisSentinelPool(jedisSentinelConfigure.getMaster(), sentinels, poolConfig);
	}
	
	// JedisSharding已废弃，新版已删除
	//@Bean
	public JedisCluster jedisShardingFactory() {
	
		ArrayNode jedisShardInfoArray = objectMapper.readValue(jedisShardInfoConfigure.getJedisShardInfo(),
				ArrayNode.class);

		Set<HostAndPort> hostAndPortList = new HashSet<>();
		for (int i = 0; i < jedisShardInfoArray.size(); i++) {
			JsonNode node = jedisShardInfoArray.get(i);

			String host = node.get("host").asString();
			int port = node.get("port").asInt();
			hostAndPortList.add(new HostAndPort(host, port));
			
//			int connectionTimeout = node.get("connectionTimeout").asInt();
//			int soTimeout = node.get("soTimeout").asInt();
//			int weight = node.get("weight").asInt();

//			shards.add(new JedisShardInfo(host, port, connectionTimeout, soTimeout, weight));
		}

		return new JedisCluster(hostAndPortList);
	}
	
	
	//@Bean
	public JedisCluster jedisShardingPoolFactory() {
		
		ArrayNode jedisShardInfoArray = objectMapper.readValue(jedisShardInfoConfigure.getJedisShardInfo(),
				ArrayNode.class);

		Set<HostAndPort> hostAndPortList = new HashSet<>();
		for (int i = 0; i < jedisShardInfoArray.size(); i++) {
			JsonNode node = jedisShardInfoArray.get(i);

			String host = node.get("host").asString();
			int port = node.get("port").asInt();
			hostAndPortList.add(new HostAndPort(host, port));
			
//			int connectionTimeout = node.get("connectionTimeout").asInt();
//			int soTimeout = node.get("soTimeout").asInt();
//			int weight = node.get("weight").asInt();

//			shards.add(new JedisShardInfo(host, port, connectionTimeout, soTimeout, weight));
		}
		
		GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<Connection>();
		
		poolConfig.setMaxTotal(Integer.valueOf(jedisConfigure.getMaxTotal()));
		poolConfig.setMaxIdle(Integer.valueOf(jedisConfigure.getMaxIdle()));
		poolConfig.setMaxWait(Duration.ofMillis(Long.valueOf(jedisConfigure.getMaxWait())));
		poolConfig.setMinEvictableIdleDuration(Duration.ofMillis(Long.valueOf(jedisConfigure.getMinEvictableIdle())));
		poolConfig.setNumTestsPerEvictionRun(Integer.valueOf(jedisConfigure.getNumTestsPerEvictionRun()));
		poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(Long.valueOf(jedisConfigure.getTimeBetweenEvictionRunsMillis())));
		poolConfig.setBlockWhenExhausted(Boolean.valueOf(jedisConfigure.getBlockWhenExhausted()));
		
		// 根据需要加在配置文件中做配置
		// JedisClientConfig JedisClientConfig = DefaultJedisClientConfig.create(0, 0, 0, null, null, 0, null, false, null, null, null, null);

		JedisClientConfig JedisClientConfig = DefaultJedisClientConfig.builder().connectionTimeoutMillis(0)
				.socketTimeoutMillis(0).blockingSocketTimeoutMillis(0).user(null).password(null).database(0)
				.clientName(null).ssl(false).sslSocketFactory(null).sslParameters(null).hostnameVerifier(null)
				.hostAndPortMapper(null).build();

		return new JedisCluster(hostAndPortList, JedisClientConfig,poolConfig);
	}
	
	//@Bean
//  4.0之前
//	public ShardedJedis jedisShardedJedisFactory() throws JsonMappingException, JsonProcessingException {
//
//		ArrayNode jedisShardInfoArray = objectMapper.readValue(jedisShardInfoConfigure.getJedisShardInfo(),
//				ArrayNode.class);
//
//		List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
//		for (int i = 0; i < jedisShardInfoArray.size(); i++) {
//			JsonNode node = jedisShardInfoArray.get(i);
//
//			String host = node.get("host").asText();
//			int port = node.get("port").asInt();
//			int connectionTimeout = node.get("connectionTimeout").asInt();
//			int soTimeout = node.get("soTimeout").asInt();
//			int weight = node.get("weight").asInt();
//
//			shards.add(new JedisShardInfo(host, port, connectionTimeout, soTimeout, weight));
//		}
//
//		return new ShardedJedis(shards);
//	}
	
	
	//@Bean
//  4.0之前
//	public ShardedJedisPool jedisShardedJedisPoolFactory() throws JsonMappingException, JsonProcessingException {
//		
//		ArrayNode jedisShardInfoArray = objectMapper.readValue(jedisShardInfoConfigure.getJedisShardInfo(),
//				ArrayNode.class);
//
//		List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
//		for (int i = 0; i < jedisShardInfoArray.size(); i++) {
//			JsonNode node = jedisShardInfoArray.get(i);
//
//			String host = node.get("host").asText();
//			int port = node.get("port").asInt();
//			int connectionTimeout = node.get("connectionTimeout").asInt();
//			int soTimeout = node.get("soTimeout").asInt();
//			int weight = node.get("weight").asInt();
//
//			shards.add(new JedisShardInfo(host, port, connectionTimeout, soTimeout, weight));
//		}
//		
//		GenericObjectPoolConfig<ShardedJedis> poolConfig = new GenericObjectPoolConfig<ShardedJedis>();
//		
//		poolConfig.setMaxTotal(Integer.valueOf(jedisConfigure.getMaxTotal()));
//		poolConfig.setMaxIdle(Integer.valueOf(jedisConfigure.getMaxIdle()));
//		poolConfig.setMaxWait(Duration.ofMillis(Long.valueOf(jedisConfigure.getMaxWait())));
//		poolConfig.setMinEvictableIdleTime(Duration.ofMillis(Long.valueOf(jedisConfigure.getMinEvictableIdle())));
//		poolConfig.setNumTestsPerEvictionRun(Integer.valueOf(jedisConfigure.getNumTestsPerEvictionRun()));
//		poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(Long.valueOf(jedisConfigure.getTimeBetweenEvictionRunsMillis())));
//		poolConfig.setBlockWhenExhausted(Boolean.valueOf(jedisConfigure.getBlockWhenExhausted()));
//		
//		
//		return new ShardedJedisPool(poolConfig,shards);
//	}

	
	public static void main(String[] args) {
		
//		URI syntax
//		Redis Standalone 
//		redis://[[username:]password@]host[:  port][/database][?[timeout=timeout[d|h|m|s|ms|us|ns]] [ &database=database] [&clientName=clientName]] 
//		Redis Standalone (SSL) 
//		rediss://[[username:]password@]host [:  port][/database][? [timeout=timeout[d|h|m|s|ms|us|ns]] [ &database=database] [&clientName=clientName]] 
//		Redis Standalone (Unix Domain Sockets) 
//		redis-socket:// [[username:]password@]path[ ?[timeout=timeout[d|h|m|s|ms|us|ns]][&database=database][&clientName=clientName]] 
//		Redis Sentinel 
//		redis-sentinel://[[username:]password@]host1 [:  port1][, host2 [:port2]][, hostN [:portN]][/  database][? [timeout=timeout[d|h|m|s|ms|us|ns]] [ &sentinelMasterId=sentinelMasterId] [&database=database] [&clientName=clientName]] 
	
		Jedis jedis = new Jedis("192.168.3.14", 6379);	
		
		jedis.set("curTime", LocalDateTime.now().toString());
		//jedis.set("test", LocalDateTime.now().toString());

		System.out.println("curTime : " + jedis.get("curTime"));
		System.out.println("test : " + jedis.get("test"));
		System.out.println("redis.template : " + jedis.get("redis.template"));
		System.out.println("redis.Jedis : " + jedis.get("redis.Jedis"));
		
		jedis.close();
	}
}
